package com.googlecode.continuity.core.server.security.authentication;

import java.io.IOException;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.ui.AbstractProcessingFilter;
import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
import com.googlecode.continuity.core.server.security.SecurityUserToken;
import com.googlecode.continuity.core.server.service.UserService;

/**
 * Extends {@link org.acegisecurity.ui.webapp.AuthenticationProcessingFilter} with Continuity-specific features.
 * <p/>
 * New features include login logging and allowing direct login requests (without having a pending authorization
 * request).
 *
 * @author Peter Schmitt <aragos@aragos.de>
 */
public class ContinuityAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
// ------------------------------ FIELDS ------------------------------

  public static final String CONTINUITY_GOTO_URL_PARAM = "goto_login";
  private UserService userService;

// -------------------------- STATIC METHODS --------------------------

  /**
   * Returns the full request URL.  If this call is based on a pending authorization request gives the originally
   * requested URL.  If this call is based on a direct login request indicated by the <code>goto</code> request paramter
   * returns the goto value.  Otherwise returns null.
   *
   * @param request http request that led to this call
   * @return target URL for redirection after successful authentication
   */
  public static String obtainFullRequestUrl(HttpServletRequest request) {
    String url = AbstractProcessingFilter.obtainFullRequestUrl(request);
    if (url == null) {
      url = request.getParameter("goto");
    }
    return url;
  }

// --------------------- GETTER / SETTER METHODS ---------------------

  /**
   * Returns this filter's user service reference.
   *
   * @return user service
   */
  public UserService getUserService() {
    return userService;
  }

  /**
   * Sets a new user service reference for this filter.
   *
   * @param userService user service
   */
  public void setUserService(UserService userService) {
    this.userService = userService;
  }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface InitializingBean ---------------------

  /**
   * Initialization check, called automatically by Spring after setup.
   *
   * @throws Exception
   */
  public void afterPropertiesSet() throws Exception {
    super.afterPropertiesSet();
    if (userService == null) {
      throw new IllegalStateException("The [userService] property must be set.");
    }
  }

// -------------------------- OTHER METHODS --------------------------

  /**
   * Returns URL to be used for redirecting in case of authentication failure. If a goto target is specified also
   * adds a query parameter to indicate this situation.
   *
   * @param request   current request
   * @param exception exception describing the authentication failure
   * @return the failure URL to be redirected to
   */
  protected String determineFailureUrl(HttpServletRequest request, AuthenticationException exception) {
    String url = super.determineFailureUrl(request, exception);
    if ((request.getParameter("goto") != null && !request.getParameter("goto").equals("")) ||
        "1".equals(request.getParameter(CONTINUITY_GOTO_URL_PARAM))) {
      // todo: add setter for url param
      url += (url.contains("?") ? "&" : "?") + CONTINUITY_GOTO_URL_PARAM + "=1";
    }
    return url;
  }

  /**
   * Update a user's last seen entry on successful login.
   *
   * @param httpServletRequest  http request
   * @param httpServletResponse http response
   * @param authentication      authentication token
   * @throws IOException
   * @see com.googlecode.continuity.core.client.ContinuityUser#getLastSeen()
   */
  protected void onSuccessfulAuthentication(HttpServletRequest httpServletRequest,
                                            HttpServletResponse httpServletResponse, Authentication authentication)
      throws IOException {
    SecurityUserToken user = (SecurityUserToken) authentication.getPrincipal();
    user.getUser().setLastSeen(new Date(System.currentTimeMillis()));
    getUserService().storeUser(user);
  }
}
